﻿using System;
using System.Collections.Generic;
using System.Linq;
using LINQPad.Extensibility.DataContext;
using LINQPad.OpenAccess.Helpers;
using LINQPad.OpenAccess.Metadata.Reflection;
using Telerik.OpenAccess.Metadata;

namespace LINQPad.OpenAccess.Services
{
    using EntitySetItemInfo = Tuple<EntitySetInfo, MetaPersistentType, ExplorerItem>;

    /// <summary>
    /// DataContextSchemaProvider - helper classes
    /// </summary>
    partial class DataContextSchemaProvider
    {
        /// <summary>
        /// Schema build context.
        /// </summary>
        protected class SchemaBuildContext
        {
            #region Private

            #region - Vars

            private readonly IDictionary<MetaMember, ExplorerItem> _memberMap = new Dictionary<MetaMember, ExplorerItem>( );
            private readonly IDictionary<string, EntitySetItemInfo> _entitySetMap = new Dictionary<string, EntitySetItemInfo>( );

            #endregion

            #region - Functions

            /// <summary>
            /// Gets the key for the specified entity set.
            /// </summary>
            /// <param name="entitySet">The entity set.</param>
            /// <param name="entityType">Type of the entity.</param>
            /// <returns>The key.</returns>
            private string GetEntitySetKey( EntitySetInfo entitySet, MetaPersistentType entityType )
            {
                return String.Format( "{0}_{1}", entitySet.Name, entityType.FullName );
            }

            #endregion

            #endregion

            #region Public

            #region - Constructors

            /// <summary>
            /// Initializes a new instance of the <see cref="SchemaBuildContext" /> class.
            /// </summary>
            /// <param name="contextType">Type of the context.</param>
            /// <param name="contextMetadata">The context metadata.</param>
            public SchemaBuildContext( Type contextType, MetadataContainer contextMetadata )
            {
                Guard.ArgumentNotNull( contextType, "contextType" );
                Guard.ArgumentNotNull( contextMetadata, "contextMetadata" );

                this.ContextMetadata = contextMetadata;
                this.ContextType = contextType;
            }

            #endregion

            #region - Functions

            /// <summary>
            /// Tries to add the entity set item.
            /// </summary>
            /// <param name="entitySet">The entity set.</param>
            /// <param name="entityType">Type of the entity.</param>
            /// <param name="entitySetItem">The entity set item.</param>
            /// <returns>
            ///   <c>true</c> on success; otherwise, <c>false</c>.
            /// </returns>
            public bool TryAddEntitySetItem( EntitySetInfo entitySet, MetaPersistentType entityType, ExplorerItem entitySetItem )
            {
                Guard.ArgumentNotNull( entitySet, "entitySet" );
                Guard.ArgumentNotNull( entityType, "entityType" );
                Guard.ArgumentNotNull( entitySetItem, "entitySetItem" );

                var key = this.GetEntitySetKey( entitySet, entityType );
                if ( !this._entitySetMap.ContainsKey( key ) )
                {
                    this._entitySetMap[key] = new EntitySetItemInfo( entitySet, entityType, entitySetItem );
                    return true;
                }
                return false;
            }
            /// <summary>
            /// Tries to add the item for the specified member.
            /// </summary>
            /// <param name="member">The member.</param>
            /// <param name="item">The item.</param>
            /// <returns>
            ///   <c>true</c> on success; otherwise, <c>false</c>.
            /// </returns>
            public bool TryAddItemForMember( MetaMember member, ExplorerItem item )
            {
                Guard.ArgumentNotNull( member, "member" );
                Guard.ArgumentNotNull( item, "item" );
                if ( !this._memberMap.ContainsKey( member ) )
                {
                    this._memberMap[member] = item;
                    return true;
                }
                return false;
            }
            /// <summary>
            /// Tries the get the entity set item.
            /// </summary>
            /// <param name="entitySet">The entity set.</param>
            /// <param name="entityType">Type of the entity.</param>
            /// <param name="entitySetItem">The entity set item.</param>
            /// <returns>
            ///   <c>true</c> on success; otherwise, <c>false</c>.
            /// </returns>
            public bool TryGetEntitySetItem( EntitySetInfo entitySet, MetaPersistentType entityType, out ExplorerItem entitySetItem )
            {
                EntitySetItemInfo entitySetItemInfo;
                if ( entitySet == null
                        || entityType == null
                        || !this._entitySetMap
                                 .TryGetValue
                                    (
                                        this.GetEntitySetKey( entitySet, entityType )
                                        , out entitySetItemInfo
                                    ) )
                {
                    entitySetItem = null;
                    return false;
                }

                entitySetItem = entitySetItemInfo.Item3;
                return true;
            }
            /// <summary>
            /// Tries the get entity set item for the specified type.
            /// </summary>
            /// <param name="entityType">Type of the entity.</param>
            /// <param name="entitySetItem">The entity set item.</param>
            /// <returns>
            ///   <c>true</c> on success; otherwise, <c>false</c>.
            /// </returns>
            public bool TryGetEntitySetItemForType( MetaPersistentType entityType, out ExplorerItem entitySetItem )
            {
                entitySetItem = entityType == null
                                    ? null
                                    : this._entitySetMap
                                          .Values
                                          .Where( v => v.Item2 == entityType )
                                          .Select( v => v.Item3 )
                                          .FirstOrDefault( );

                return entitySetItem != null;
            }
            /// <summary>
            /// Tries to get the item for the specified member.
            /// </summary>
            /// <param name="member">The member.</param>
            /// <param name="item">The item.</param>
            /// <returns>
            ///   <c>true</c> on success; otherwise, <c>false</c>.
            /// </returns>
            public bool TryGetItemForMember( MetaMember member, out ExplorerItem item )
            {
                if ( member == null
                        || !this._memberMap.TryGetValue( member, out item ) )
                {
                    item = null;
                    return false;
                }
                return true;
            }

            #endregion

            #region - Properties

            /// <summary>
            /// Gets the type of the open access context.
            /// </summary>
            /// <value>
            /// The type of the open access context.
            /// </value>
            public Type ContextType { get; private set; }
            /// <summary>
            /// Gets the entity set item count.
            /// </summary>
            /// <value>
            /// The entity set item count.
            /// </value>
            public int EntitySetItemCount
            {
                get
                {
                    return this._entitySetMap.Count;
                }
            }
            /// <summary>
            /// Gets the entity set items.
            /// </summary>
            /// <value>
            /// The entity set items.
            /// </value>
            public IEnumerable<ExplorerItem> EntitySetItems
            {
                get
                {
                    return this._entitySetMap
                               .Values
                               .Select( v => v.Item3 );
                }
            }
            /// <summary>
            /// Gets the metadata of the open access context.
            /// </summary>
            /// <value>
            /// The metadata of the open access context.
            /// </value>
            public MetadataContainer ContextMetadata
            {
                get;
                private set;
            }

            #endregion

            #endregion
        }

    }
}
